/*
 * DERECHOS DE AUTOR (C) 2014 POR Jose Carlos Palma.
 *
 * TODOS LOS DERECHOS RESERVADOS. NINGUNA PARTE  DE ESTE CÓDIGO FUENTE 
 * PUEDE SER REPRODUCIDA O TRANSMITIDA O UTILIZADA EN  CUALQUIER FORMA 
 * O POR CUALQUIER  MEDIO, YA SEA  ELECTRÓNICO O MECÁNICO,  INCLUYENDO 
 * EL FOTOCOPIADO, GRABACIÓN O POR CUALQUIER SISTEMA DE ALMACENAMIENTO 
 * O RECUPERACIÓN DE INFORMACIÓN SIN  EL PERMISO PREVIO Y  POR ESCRITO
 * DEL PROPIETARIO DE LOS DERECHOS DE AUTOR.
 */
package org.dwh.pivot;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.Level;

/**
 *
 * @author Jose Carlos Palma
 * @version v1.0
 */
public class Pivotator extends Closer {

    private Excel xls;
    private Conexion cxn;
    private boolean tmp = true;
    private boolean zip = false;
    private boolean close = false;
    private boolean refreshAll = false;

    /**
     * Crea una instancia de Pivotator.
     *
     * @param args the command line arguments
     * @see org.dwh.pivot.Pivotator#getJavaPID()
     * @see org.dwh.pivot.Pivotator#argumentParser(java.lang.String[])
     * @see org.dwh.pivot.Pivotator#loadConfigFile()
     */
    private Pivotator(String[] args) {
        super();
        getJavaPID();
        argumentParser(args);
        loadConfigFile();

        //Hilo que cierra todas la conexion y la aplicacion de Excel en caso
        //que el proceso sea killiado (ctrl+c)
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                if (!close) {
                    Pivotator.log(Level.INFO, "Liberando los recursos...");
                    Pivotator.this.closing();
                    Pivotator.log(Level.INFO, "##     ACTUALIZACION ABORTADA     ##");
                }
            }
        });

    }

    /**
     * Obtiene el PID (Process Identification) de la aplicación (JPivotator).
     */
    private void getJavaPID() {
        try {
            pid = Integer.valueOf(ManagementFactory.getRuntimeMXBean().getName().split("@")[0]);
        } catch (NumberFormatException ex) {
            log("ERROR", "No se pudo obtener el PID de la aplicacion. {0}", ex.getMessage());
            closing();
            System.exit(IExitCode.CANNOT_GET_PID);
        }
    }

    /**
     * Analiza gramáticamente los argumentos para obtener el pkCubo.
     *
     * @param args the command line arguments.
     */
    private void argumentParser(String[] args) {
        //Verifica que venga el argumento de pkCubo
        if (args.length < 1) {
            log("ERROR", "Falta el argumento pkCubo");
            closing();
            System.exit(IExitCode.MISSING_ARGUMENTS_PK_CUBO);
        }
        try {
            Pivotator.pkCubo = Integer.parseInt(args[0]);
        } catch (NumberFormatException ex) {
            log("ERROR", "El argumento pkCubo no es un numero valido. {0}", ex.getMessage());
            closing();
            System.exit(IExitCode.INVALID_ARGUMENT_PK_CUBO);
        }

        for (String arg : args) {
            if (arg.matches("t[e]?mp([ ]*[=][ ]*false)?")) {
                this.tmp = false;
            } else if (arg.matches("zip([ ]*[=][ ]*true)?")) {
                this.zip = true;
            }
        }
    }

    /**
     * Carga el archivo de configuración.
     */
    private void loadConfigFile() {
        //Carga el archivo de configuracion
        try {
            Pivotator.log(Level.INFO, "Cargando archivo de configuracion...");
            System.getProperties().load(new FileInputStream("pivot.conf"));
        } catch (IOException ex) {
            Pivotator.log("ERROR", "No se pudo carga el archivo de configuracion. {0}", ex);
            closing();
            System.exit(IExitCode.CONFIGURATION_FILE_NOT_EXISTS);
        }
    }

    /**
     * Ejecuta el Pivotator.
     */
    public void execute() {
        long t0 = System.currentTimeMillis();
        int motivo = IExitCode.SUCCESSFULLY_EXECUTED;

        try {

            this.cxn = new Conexion("oracle");
            add(cxn);

            //Obtiene la secuencia del log.
            Pivotator.seq = this.cxn.getLogSecuencia();

            //Escribe al log inicio
            cxn.logInicio(seq, pkCubo, pid, pidExcel);

            this.xls = new Excel();
            add(xls);

            Cubo cubo = this.cxn.getCubo(pkCubo);

            //Pivotator.size = cubo.getSize();
            //xls.open(cubo.getFullName());

            createFiles(cubo.getFullName());

            if (tmp) {
                createTemporaryFile();
                xls.open(tmpFile.getAbsolutePath());

            } else {
                xls.open(srcFile.getAbsolutePath());
            }
            
            Pivotator.pidExcel = xls.getPID();

            if (xls.isReadOnly()) {
                throw new PivotatorlException("El archivo " + cubo.getFullName() + " esta en uso.", new Exception(), IExitCode.WORKBOOK_IN_USE);
            }

            //Actualiza las PivotTables
            xls.refresh();
            
            xls.save();
            
            xls.close();

            if (tmp) {
                replaceCube();
            }

            long t1 = System.currentTimeMillis();

            Pivotator.log(Level.INFO, "Duracion {0}seg", "Tiempo: " + String.valueOf((t1 - t0) / 1000));

            //Escribe al log fin
            cxn.logFin(seq, "S", "", Pivotator.size);


        } catch (PivotatorlException ex) {
            if (cxn != null) {
                cxn.logFin(seq, "N", ex.getMessage(), 0);
            }
            log("ERROR", ex.getMessage());
            closing();
            System.exit(ex.getMotivo());
        } catch (Exception ex) {
            if (cxn != null) {
                cxn.logFin(seq, "N", ex.getMessage(), 0);
            }
            log("ERROR", ex.getMessage());
            closing();
            System.exit(IExitCode.GLOBAL_ERROR);
        }

        closing();
        close = true;
    }

    /**
     * Crea una instancia del Pivotator (Singleton Patter Design).
     *
     * @param args the command line arguments.
     * @return una instancia de Pivotator
     */
    public static Pivotator createInstance(String[] args) {
        if (_intance == null) {
            _intance = new Pivotator(args);
        }
        return _intance;
    }

    /**
     * Devuelve la instancia única de Pivotator.
     *
     * @return una instancia de Pivotator
     */
    public static Pivotator getInstance() {
        return _intance;
    }

    /**
     * Crea los objetos File del cubo y el archivo temporal.
     *
     * @param fileName
     */
    public void createFiles(String fileName) {
        //Crea el objeto File del archivo original
        srcFile = new File(fileName);

        //Crea el objeto File del archivo temporal
        tmpFile = new File(System.getProperty("cubo.tmp") + "/" + pkCubo + "_" + srcFile.getName());

        if (tmpFile.exists()) {
            tmpFile.delete();
        }
    }

    /**
     * Crea un archivo temporal basado en el archivo de Excel original.
     */
    public void createTemporaryFile() {

        Path src = srcFile.toPath();
        Path dst = tmpFile.toPath();

        try {
            Pivotator.log(Level.INFO, "Creando archivo temporal...");
            Files.copy(src, dst, StandardCopyOption.REPLACE_EXISTING);
        } catch (IOException ex) {
            throw new PivotatorlException("No se pudo crear el archivo temporal.", ex, IExitCode.ERROR_COPYING_TEMPORARY_FILE);
        }

    }
    
    public boolean isRefreshAll(){
        return refreshAll;
    }

    /**
     * Reemplaza el archivo original con el archivo temporal ya actualizado.
     */
    public void replaceCube() {
        Path src = tmpFile.toPath();
        Path dst = srcFile.toPath();

        try {
            Pivotator.log(Level.INFO, "Reemplazando el cubo original...");
            Files.copy(src, dst, StandardCopyOption.REPLACE_EXISTING);
        } catch (IOException ex) {
            throw new PivotatorlException("No se pudo crear el archivo temporal.", ex, IExitCode.ERROR_REPLACING_ORIGINAL_FILE);
        }

        if (tmpFile.exists()) {

            String r = (tmpFile.delete() ? "[OK]" : "[FAILED]");
            Pivotator.log(Level.INFO, "Borrando archivo temporal \"{0}\"...{1}", tmpFile.getName(), r);

        }

    }

    /**
     * Escritura de log.
     *
     * @param level nivel del log (Info, Warning, Error).
     * @param mess es el mensaje del log.
     * @param args son los argumentos del mensaje (utiliza MessageFormat)
     * @see java.util.logging.Level
     * @see java.text.MessageFormat#format(java.lang.String, java.lang.Object[])
     */
    public static void log(Level level, String mess, Object... args) {
        log(level.toString(), mess, args);
    }

    /**
     * Escritura de log.
     *
     * @param level nivel del log (Info, Warning, Error).
     * @param mess es el mensaje del log.
     * @param args son los argumentos del mensaje (utiliza MessageFormat)
     * @see java.text.MessageFormat#format(java.lang.String, java.lang.Object[])
     */
    public static void log(String level, String mess, Object... args) {
        StringBuilder sb = new StringBuilder(df.format(new Date()));
        sb.append(" [").append(pkCubo).append(",");
        sb.append(pid).append(",").append(pidExcel).append("] >>> ");
        sb.append(MessageFormat.format(level.toUpperCase() + ": " + mess, args));
        System.out.println(sb.toString());
    }
    /**
     * Objeto File correspondiente al archivo de Excel.
     */
    private File srcFile;
    /**
     * Objeto File correspondiente al archivo temporal.
     */
    private File tmpFile;
    /**
     *
     */
    private static SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    /**
     *
     */
    private static Pivotator _intance = null;
    /**
     *
     */
    private static int pkCubo = -1;
    /**
     *
     */
    private static int pid = -1;
    /**
     *
     */
    private static int pidExcel = -1;
    /**
     *
     */
    private static long seq = -1;
    private static long size = 0;
}
